package com.ehome.plugin

import com.ehome.plugin.util.Log
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.AdviceAdapter


class EHomeApplicationClassVisitor(api: Int, classVisitor: ClassVisitor?) : ClassVisitor(api, classVisitor) {
    private val TAG = EHomeApplicationClassVisitor::class.simpleName

    private var hasAppOnAttachMethod = false
    private var hasAppOnCreateMethod = false
    private var name: String? = null
    private var superName: String? = null

    constructor(classVisitor: ClassVisitor?) : this(api = Opcodes.ASM5, classVisitor)

    override fun visit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array<out String>?) {
        super.visit(version, access, name, signature, superName, interfaces)
        this.name = name
        this.superName = superName

        println("$TAG#visit: name-> $name")


    }


    override fun visitMethod(access: Int, name: String?, descriptor: String?, signature: String?, exceptions: Array<out String>?): MethodVisitor {
        println("$TAG#visitMethod: name-> $name")
        if (("attachBaseContext" == name) and ("(Landroid/content/Context;)V" == descriptor)) {
            hasAppOnAttachMethod = true
            val visitMethod = cv.visitMethod(access, name, descriptor, signature, exceptions)
            return object : AdviceAdapter(api, visitMethod, access, name, descriptor) {
                override fun onMethodEnter() {
                    super.onMethodEnter()
                    mv.visitVarInsn(Opcodes.ALOAD, 0)
                    mv.visitLdcInsn("app_attachBaseContext")
                    mv.visitInsn(Opcodes.ICONST_1)
                    mv.visitMethodInsn(
                        Opcodes.INVOKESTATIC,
                        "com/efs/sdk/launch/LaunchManager",
                        "onTraceApp",
                        "(Landroid/app/Application;Ljava/lang/String;Z)V",
                        false
                    )
                }

                override fun onMethodExit(opcode: Int) {
                    super.onMethodExit(opcode)
                    mv.visitVarInsn(Opcodes.ALOAD, 0)
                    mv.visitLdcInsn("app_attachBaseContext")
                    mv.visitInsn(Opcodes.ICONST_0)
                    mv.visitMethodInsn(
                        Opcodes.INVOKESTATIC,
                        "com/efs/sdk/launch/LaunchManager",
                        "onTraceApp",
                        "(Landroid/app/Application;Ljava/lang/String;Z)V",
                        false
                    )
                }
            }
        } else if (("onCreate" == name) and ("()V" == name)) {
            hasAppOnCreateMethod = true
            val visitMethod = cv.visitMethod(access, name, descriptor, signature, exceptions)
            return object : AdviceAdapter(api, visitMethod, access, name, descriptor) {
                override fun onMethodEnter() {
                    super.onMethodEnter()
                }

                override fun onMethodExit(opcode: Int) {
                    super.onMethodExit(opcode)
                    mv.visitVarInsn(Opcodes.ALOAD, 0)
                    mv.visitLdcInsn("app_onCreate")
                    mv.visitInsn(Opcodes.ICONST_0)
                    mv.visitMethodInsn(
                        Opcodes.INVOKESTATIC,
                        "com/efs/sdk/launch/LaunchManager",
                        "onTraceApp",
                        "(Landroid/app/Application;Ljava/lang/String;Z)V",
                        false
                    )
                }
            }
        } else {
            return super.visitMethod(access, name, descriptor, signature, exceptions)
        }
    }

    override fun visitEnd() {
        if (!hasAppOnAttachMethod) {
            insertAppOnAttachMethod(cv, superName)
        }

        if (!hasAppOnCreateMethod) {
            insertAppOnCreateMethod(cv, superName)
        }
        super.visitEnd()
    }


    private fun insertAppOnAttachMethod(cv: ClassVisitor, superName: String?) {
        val methodVisitor =
            cv.visitMethod(Opcodes.ACC_PROTECTED, "attachBaseContext", "(Landroid/content/Context;)V", null, null)
        methodVisitor.visitCode()
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0)
        methodVisitor.visitLdcInsn("app_attachBaseContext")
        methodVisitor.visitInsn(Opcodes.ICONST_1)
        methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "com/efs/sdk/launch/LaunchManager", "onTraceApp", "(Landroid/app/Application;Ljava/lang/String;Z)V", false)
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0)
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 1)
        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "attachBaseContext", "(Landroid/content/Context;)V", false)
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0)
        methodVisitor.visitLdcInsn("app_attachBaseContext")
        methodVisitor.visitInsn(Opcodes.ICONST_0)
        methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "com/efs/sdk/launch/LaunchManager", "onTraceApp", "(Landroid/app/Application;Ljava/lang/String;Z)V", false)
        methodVisitor.visitInsn(Opcodes.RETURN)
        methodVisitor.visitMaxs(3, 2)
        methodVisitor.visitEnd()
    }

    private fun insertAppOnCreateMethod(cv: ClassVisitor, superName: String?) {
        val methodVisitor = cv.visitMethod(Opcodes.ACC_PUBLIC, "onCreate", "()V", null, null)
        methodVisitor.visitCode()
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0)
        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "onCreate", "()V", false)
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0)
        methodVisitor.visitLdcInsn("app_onCreate")
        methodVisitor.visitInsn(Opcodes.ICONST_0)
        methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "com/efs/sdk/launch/LaunchManager", "onTraceApp", "(Landroid/app/Application;Ljava/lang/String;Z)V", false)
        methodVisitor.visitInsn(Opcodes.RETURN)
        methodVisitor.visitMaxs(3, 1)
        methodVisitor.visitEnd()
    }


}